【Clojure入門】 Leiningenでプログラムをコンパイル・実行する
Clojure開発では、前節で簡単に触れたようにREPLを活用して素早く動作確認しながら進めていくのが一般的ですが、LeiningenなどのビルドツールでClojureプログラムを明示的にコンパイルして実行することもできます。
まずはREPLの場合と同様にHello, World!するプログラムを書いてみましょう。
code:src/sandbox/hello_world.clj
(ns sandbox.hello-world)
(defn -main args
(println "Hello, World!"))
ns や defn といった新たなキーワードが登場しましたが(詳しくは後ほど説明していきます)、 ファイル階層に対応した sandbox.hello-world という名前のモジュール(名前空間)で、アプリケーションのエントリーポイントとなるmain関数を定義していることが何となく読み取れるかもしれません。
defn は (defn <関数名> [<引数>] <本体>) という関数定義のための特殊形式で、本体部分は先ほどREPLで実行したものと同じ (println "Hello, World!") です。
上記のプログラムを hello_world.clj というファイルに保存してLeiningenでコンパイル・実行してみましょう。
ここでは、 sandbox という名前のプロジェクトとして次のようにファイルを配置します。
Clojureでは ns による名前空間名の区切り文字に - (hyphen)、ファイル名の区切り文字に _ (underscore)を利用することに注意が必要です: e.g. (ns sandbox.hello-world) vs sandbox/hello_world.clj
code:bash
$ tree sandbox
sandbox
├── project.clj
└── src
└── sandbox
└── hello_world.clj
2 directories, 2 files
そして sandbox ディレクトリ直下にプロジェクト設定ファイル project.clj を作成します。
code:project.clj
(defproject sandbox "0.1.0"
:dependencies org.clojure/clojure "1.10.1"
:profiles {:dev {:dependencies com.bhauman/rebel-readline "0.1.4"
準備ができたところで sandbox ディレクトリに移動し lein run -m sandbox.hello-world というコマンドを実行してみましょう。
ここまでの設定が上手くいっていれば、 hello_world.clj がコンパイルされ、 -main 関数が実行されて Hello, World! と出力されます。
code:bash
$ cd sandbox/
$ lein run -m sandbox.hello-world
Hello, World!
lein run はLeiningenで -main 関数を実行するためのコマンドで、ここでは -m sandbox.hello-world というオプションで -main 関数が定義されている名前空間を指定しています。
Scalaの sbt run と異なり lein run には自動的に -main 関数を探して実行する仕組みはありませんが、 project.clj に以下のように :main でデフォルトのエントリーポイントとなる名前空間を指定することができ、
code:project.clj
(defproject sandbox "0.1.0"
:dependencies org.clojure/clojure "1.10.1"
:main sandbox.hello-world ; -main関数の属する名前空間を指定することができる
:profiles {:dev {:dependencies com.bhauman/rebel-readline "0.1.4"
この設定があると単に lein run でプログラムを実行することができます。
code:bash
$ lein run
Hello, World!
Leiningenで管理されているClojureプロジェクトでは、 lein repl で(rebel-readlineを設定していれば lein rebel でも)起動するREPLからプロジェクト内のプログラムに直接アクセスすることができます。
hello_world.clj と同じ階層に user.clj というファイルを作りましょう。
code:src/sandbox/user.clj
(ns sandbox.user)
(println (str name " " age)))
user.clj では、 :name, :age というキーを持つ連想データ(associative data; マップなど)を受け取り、それぞれの値をスペースで文字列結合してプリントする関数 print-user を定義しています({:keys [name age]} という記法など詳しくは後の節で説明します)。
code:bash
$ tree -I target .
.
├── project.clj
└── src
└── sandbox
├── hello_world.clj
└── user.clj
2 directories, 3 files
この状態で lein rebel (または lein repl)でREPLを起動すると、ここまでに定義した関数を利用することができます。
code:clojure
nil
user=> (def u {:name "You Watanabe" :age 17})
user=> (user/print-user u)
You Watanabe 17
nil
(require '[sandbox.user :as user]) で名前空間 sandbox.user をロードして user という別名でアクセスできるようにし、マップのリテラル {:name "You Watanabe" :age 17} に u と名前を付け、 user/print-user 関数を u に適用しています。
このように、Clojure REPLからプロジェクトで定義済みのものに自由にアクセスすることができます。